﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLite.Reflection;
using System.Collections;

namespace NLite.Mapping.Internal
{
    class DictionaryMapper:MapperBase
    {
        readonly Type fromType, toType;

        static readonly Type KvpType = typeof(KeyValuePair<,>);

        public DictionaryMapper(Type fromType, Type toType)
            : base(fromType, toType)
        {
            this.fromType = fromType;
            this.toType = toType;
        }

        struct DictionaryInfo
        {
            public Type Type;
            public Type Key;
            public Type Value;
            public Type Kvp;


            public bool IsGeneric
            {
                get { return Type != null && typeof(DictionaryEntry) != Kvp; }
            }
        }

        public override void Map(ref object from, ref object to)
        {
            var sourceEnumerableValue = (IEnumerable)from;

            var sourceInfo = new DictionaryInfo();
            var destInfo = new DictionaryInfo();

            GetDictionaryInfo(fromType,ref sourceInfo);
            GetDictionaryInfo(toType, ref destInfo);

            if (to == null)
            {
                if (destInfo.IsGeneric)
                    to = ObjectCreator.Create(typeof(Dictionary<,>).MakeGenericType(destInfo.Key, destInfo.Value));
                else
                    to = ObjectCreator.Create(destInfo.Type);
            }

            var addMethod = destInfo.Type.GetMethod("Add", new Type[] { destInfo.Key, destInfo.Value }).GetAction<object,object>();
            var getKey = sourceInfo.Kvp.GetProperty("Key").GetGetter<object>();
            var getValue = sourceInfo.Kvp.GetProperty("Value").GetGetter<object>();


            foreach (var item in sourceEnumerableValue)
            {
                var key = getKey(item);
                var value = getValue(item);

                var dstKey = Mapper.Map(key, Types.Object, destInfo.Key);
                var dstValue = Mapper.Map(value, Types.Object, destInfo.Value);
                
                addMethod(to, dstKey, dstValue);
            }
        }

      
        private static void GetDictionaryInfo(Type type, ref DictionaryInfo info)
        {
            if (TypeHelper.IsGenericDictionaryType(type))
            {
                info.Type = type.GetGenericDictionaryType();
                info.Key = info.Type.GetGenericArguments()[0];
                info.Value = info.Type.GetGenericArguments()[1];
                info.Kvp = KvpType.MakeGenericType(info.Key, info.Value);
            }
            else
            {
                info.Type = type;
                info.Key = Types.Object;
                info.Value = Types.Object;
                info.Kvp = typeof(DictionaryEntry);
            }
        }

   
    }
}
